package com.coupon.admin.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.coupon.admin.dto.CouponReceiveDTO;
import com.coupon.admin.dto.CouponGroupCreateDTO;
import com.coupon.admin.entity.Coupon;
import com.coupon.admin.entity.UserCoupon;
import com.coupon.admin.entity.CouponGroup;
import com.coupon.admin.entity.CouponGroupMember;
import com.coupon.admin.entity.User;
import com.coupon.admin.mapper.UserCouponMapper;
import com.coupon.admin.mapper.CouponMapper;
import com.coupon.admin.mapper.CouponGroupMapper;
import com.coupon.admin.mapper.CouponGroupMemberMapper;
import com.coupon.admin.mapper.UserMapper;
import com.coupon.admin.service.CouponReceiveService;
import com.coupon.admin.service.CouponService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 优惠券发放与领取服务实现类
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class CouponReceiveServiceImpl extends ServiceImpl<UserCouponMapper, UserCoupon> 
        implements CouponReceiveService {
    
    private final CouponService couponService;
    private final CouponMapper couponMapper;
    private final CouponGroupMapper couponGroupMapper;
    private final CouponGroupMemberMapper couponGroupMemberMapper;
    private final UserMapper userMapper;
    private final RedisTemplate<String, Object> redisTemplate;
    
    private static final String COUPON_STOCK_KEY = "coupon:stock:";
    private static final String USER_RECEIVE_LIMIT_KEY = "coupon:receive:limit:";
    private static final String COUPON_LOCK_KEY = "coupon:lock:";
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserCoupon receiveCoupon(CouponReceiveDTO receiveDTO) {
        Long userId = receiveDTO.getUserId();
        Long couponId = receiveDTO.getCouponId();
        
        // 获取分布式锁
        String lockKey = COUPON_LOCK_KEY + couponId + ":" + userId;
        Boolean lockAcquired = redisTemplate.opsForValue()
                .setIfAbsent(lockKey, "1", 30, TimeUnit.SECONDS);
        
        if (!lockAcquired) {
            throw new RuntimeException("领取过于频繁，请稍后再试");
        }
        
        try {
            // 校验用户和券
            validateReceiveCoupon(userId, couponId);
            
            // 检查库存
            if (!checkAndReduceStock(couponId)) {
                throw new RuntimeException("券库存不足");
            }
            
            // 检查用户领取限制
            if (!checkUserReceiveLimit(userId, couponId)) {
                throw new RuntimeException("已达到领取上限");
            }
            
            // 创建用户券
            UserCoupon userCoupon = createUserCoupon(userId, couponId, receiveDTO);
            
            // 更新券统计
            updateCouponStats(couponId);
            
            // 设置用户领取限制
            setUserReceiveLimit(userId, couponId);
            
            log.info("用户{}成功领取券{}", userId, couponId);
            return userCoupon;
            
        } finally {
            // 释放锁
            redisTemplate.delete(lockKey);
        }
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean autoIssueCoupon(Long userId, Long couponId, String reason) {
        try {
            CouponReceiveDTO receiveDTO = new CouponReceiveDTO();
            receiveDTO.setUserId(userId);
            receiveDTO.setCouponId(couponId);
            receiveDTO.setSource("AUTO");
            
            UserCoupon userCoupon = receiveCoupon(receiveDTO);
            
            // 记录自动发放日志
            log.info("系统自动发放券{}给用户{}，原因：{}", couponId, userId, reason);
            
            return userCoupon != null;
        } catch (Exception e) {
            log.error("自动发放券失败，用户ID：{}，券ID：{}", userId, couponId, e);
            return false;
        }
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean batchIssueCoupons(List<Long> userIds, Long couponId, String reason) {
        int successCount = 0;
        
        for (Long userId : userIds) {
            try {
                if (autoIssueCoupon(userId, couponId, reason)) {
                    successCount++;
                }
            } catch (Exception e) {
                log.error("批量发放券失败，用户ID：{}，券ID：{}", userId, couponId, e);
            }
        }
        
        log.info("批量发放券完成，券ID：{}，成功：{}，总数：{}", couponId, successCount, userIds.size());
        return successCount > 0;
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createCouponGroup(CouponGroupCreateDTO createDTO) {
        Long couponId = createDTO.getCouponId();
        Long initiatorId = createDTO.getInitiatorId();
        
        // 校验券是否支持拼团
        Coupon coupon = couponService.getById(couponId);
        if (coupon == null || !Boolean.TRUE.equals(coupon.getSupportGroup())) {
            throw new RuntimeException("该券不支持拼团");
        }
        
        // 校验发起人
        User initiator = userMapper.selectById(initiatorId);
        if (initiator == null) {
            throw new RuntimeException("发起人不存在");
        }
        
        // 创建拼券团
        CouponGroup group = new CouponGroup();
        group.setCouponId(couponId);
        group.setInitiatorId(initiatorId);
        group.setStatus("WAITING");
        group.setTargetSize(createDTO.getTargetSize());
        group.setCurrentSize(1);
        group.setRewardMultiplier(coupon.getGroupRewardMultiplier());
        group.setExpireTime(LocalDateTime.now().plusHours(24)); // 24小时过期
        
        couponGroupMapper.insert(group);
        
        // 添加发起人到团成员
        CouponGroupMember member = new CouponGroupMember();
        member.setGroupId(group.getId());
        member.setUserId(initiatorId);
        member.setJoinTime(LocalDateTime.now());
        member.setIsHelper(false);
        member.setReward(BigDecimal.ZERO);
        
        couponGroupMemberMapper.insert(member);
        
        log.info("创建拼券团成功，团ID：{}，券ID：{}，发起人：{}", group.getId(), couponId, initiatorId);
        return group.getId();
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean joinCouponGroup(Long groupId, Long userId) {
        // 获取拼券团信息
        CouponGroup group = couponGroupMapper.selectById(groupId);
        if (group == null) {
            throw new RuntimeException("拼券团不存在");
        }
        
        if (!"WAITING".equals(group.getStatus())) {
            throw new RuntimeException("拼券团已结束");
        }
        
        if (group.getExpireTime().isBefore(LocalDateTime.now())) {
            throw new RuntimeException("拼券团已过期");
        }
        
        if (group.getCurrentSize() >= group.getTargetSize()) {
            throw new RuntimeException("拼券团已满");
        }
        
        // 检查用户是否已加入
        LambdaQueryWrapper<CouponGroupMember> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(CouponGroupMember::getGroupId, groupId)
               .eq(CouponGroupMember::getUserId, userId);
        
        if (couponGroupMemberMapper.selectCount(wrapper) > 0) {
            throw new RuntimeException("您已加入该拼券团");
        }
        
        // 添加用户到团成员
        CouponGroupMember member = new CouponGroupMember();
        member.setGroupId(groupId);
        member.setUserId(userId);
        member.setJoinTime(LocalDateTime.now());
        member.setIsHelper(true);
        member.setReward(BigDecimal.ZERO);
        
        couponGroupMemberMapper.insert(member);
        
        // 更新团信息
        group.setCurrentSize(group.getCurrentSize() + 1);
        
        // 检查是否成团
        if (group.getCurrentSize().equals(group.getTargetSize())) {
            group.setStatus("SUCCESS");
            group.setSuccessTime(LocalDateTime.now());
            
            // 发放奖励券
            distributeGroupRewards(group);
        }
        
        couponGroupMapper.updateById(group);
        
        log.info("用户{}加入拼券团{}成功", userId, groupId);
        return true;
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserCoupon completeBlindBoxTask(Long userId, Long taskId) {
        // TODO: 实现盲盒任务完成逻辑
        // 1. 校验任务是否存在且有效
        // 2. 检查用户是否已完成任务
        // 3. 随机抽取奖励券
        // 4. 发放奖励券
        // 5. 更新任务完成状态
        
        throw new RuntimeException("盲盒任务功能待实现");
    }
    
    @Override
    public List<UserCoupon> getUserCoupons(Long userId, String status) {
        LambdaQueryWrapper<UserCoupon> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserCoupon::getUserId, userId);
        
        if (status != null) {
            wrapper.eq(UserCoupon::getStatus, status);
        }
        
        wrapper.orderByDesc(UserCoupon::getReceiveTime);
        
        return list(wrapper);
    }
    
    @Override
    public List<Long> getPersonalizedCoupons(Long userId, Integer limit) {
        // TODO: 实现AI个性化推荐逻辑
        // 1. 获取用户画像
        // 2. 基于协同过滤算法推荐
        // 3. 结合用户行为数据
        // 4. 返回推荐券ID列表
        
        // 临时实现：返回随机券
        LambdaQueryWrapper<Coupon> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Coupon::getStatus, "PUBLISHED")
               .gt(Coupon::getValidEndTime, LocalDateTime.now())
               .last("LIMIT " + limit);
        
        List<Coupon> coupons = couponMapper.selectList(wrapper);
        return coupons.stream().map(Coupon::getId).toList();
    }
    
    @Override
    public boolean canReceiveCoupon(Long userId, Long couponId) {
        try {
            validateReceiveCoupon(userId, couponId);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
    
    @Override
    public Integer getCouponStock(Long couponId) {
        String stockKey = COUPON_STOCK_KEY + couponId;
        Object stock = redisTemplate.opsForValue().get(stockKey);
        
        if (stock != null) {
            return (Integer) stock;
        }
        
        // 从数据库获取
        Coupon coupon = couponService.getById(couponId);
        if (coupon != null) {
            int remainingStock = coupon.getTotalStock() - coupon.getIssuedCount();
            // 缓存到Redis
            redisTemplate.opsForValue().set(stockKey, remainingStock, 5, TimeUnit.MINUTES);
            return remainingStock;
        }
        
        return 0;
    }
    
    @Override
    public void processExpiredCoupons() {
        // 查询即将过期的券
        LambdaQueryWrapper<UserCoupon> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserCoupon::getStatus, "UNUSED")
               .lt(UserCoupon::getExpireTime, LocalDateTime.now());
        
        List<UserCoupon> expiredCoupons = list(wrapper);
        
        for (UserCoupon userCoupon : expiredCoupons) {
            userCoupon.setStatus("EXPIRED");
            updateById(userCoupon);
            
            log.info("券{}已过期，用户：{}", userCoupon.getCouponId(), userCoupon.getUserId());
        }
    }
    
    /**
     * 校验领取券的条件
     */
    private void validateReceiveCoupon(Long userId, Long couponId) {
        // 校验用户
        User user = userMapper.selectById(userId);
        if (user == null || !"ACTIVE".equals(user.getStatus())) {
            throw new RuntimeException("用户不存在或状态异常");
        }
        
        // 校验券
        Coupon coupon = couponService.getById(couponId);
        if (coupon == null) {
            throw new RuntimeException("券不存在");
        }
        
        if (!"PUBLISHED".equals(coupon.getStatus())) {
            throw new RuntimeException("券未发布");
        }
        
        if (coupon.getValidEndTime().isBefore(LocalDateTime.now())) {
            throw new RuntimeException("券已过期");
        }
        
        if (coupon.getIssuedCount() >= coupon.getTotalStock()) {
            throw new RuntimeException("券已发完");
        }
    }
    
    /**
     * 检查并减少库存
     */
    private boolean checkAndReduceStock(Long couponId) {
        String stockKey = COUPON_STOCK_KEY + couponId;
        
        // 使用Redis原子操作减少库存
        Long stock = redisTemplate.opsForValue().decrement(stockKey);
        
        if (stock != null && stock >= 0) {
            return true;
        }
        
        // 如果Redis中没有库存信息，从数据库检查
        Coupon coupon = couponService.getById(couponId);
        if (coupon != null) {
            int remainingStock = coupon.getTotalStock() - coupon.getIssuedCount();
            if (remainingStock > 0) {
                // 初始化Redis库存
                redisTemplate.opsForValue().set(stockKey, remainingStock - 1, 5, TimeUnit.MINUTES);
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * 检查用户领取限制
     */
    private boolean checkUserReceiveLimit(Long userId, Long couponId) {
        String limitKey = USER_RECEIVE_LIMIT_KEY + userId + ":" + couponId;
        Object count = redisTemplate.opsForValue().get(limitKey);
        
        if (count != null) {
            return (Integer) count < getCouponPerUserLimit(couponId);
        }
        
        // 从数据库检查
        LambdaQueryWrapper<UserCoupon> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserCoupon::getUserId, userId)
               .eq(UserCoupon::getCouponId, couponId);
        
        int receivedCount = Math.toIntExact(count(wrapper));
        return receivedCount < getCouponPerUserLimit(couponId);
    }
    
    /**
     * 设置用户领取限制
     */
    private void setUserReceiveLimit(Long userId, Long couponId) {
        String limitKey = USER_RECEIVE_LIMIT_KEY + userId + ":" + couponId;
        redisTemplate.opsForValue().increment(limitKey);
        redisTemplate.expire(limitKey, 1, TimeUnit.DAYS);
    }
    
    /**
     * 获取券的每人限领数量
     */
    private Integer getCouponPerUserLimit(Long couponId) {
        Coupon coupon = couponService.getById(couponId);
        return coupon != null ? coupon.getPerUserLimit() : 1;
    }
    
    /**
     * 创建用户券
     */
    private UserCoupon createUserCoupon(Long userId, Long couponId, CouponReceiveDTO receiveDTO) {
        Coupon coupon = couponService.getById(couponId);
        
        UserCoupon userCoupon = new UserCoupon();
        userCoupon.setUserId(userId);
        userCoupon.setCouponId(couponId);
        userCoupon.setCouponCode(generateCouponCode());
        userCoupon.setStatus("UNUSED");
        userCoupon.setReceiveTime(LocalDateTime.now());
        userCoupon.setExpireTime(coupon.getValidEndTime());
        userCoupon.setFromGroup("GROUP".equals(receiveDTO.getSource()));
        userCoupon.setGroupId(receiveDTO.getGroupId());
        userCoupon.setFromBlindBox("BLIND_BOX".equals(receiveDTO.getSource()));
        userCoupon.setBlindBoxTaskId(receiveDTO.getBlindBoxTaskId());
        
        save(userCoupon);
        return userCoupon;
    }
    
    /**
     * 生成券码
     */
    private String generateCouponCode() {
        return UUID.randomUUID().toString().replace("-", "").toUpperCase();
    }
    
    /**
     * 更新券统计
     */
    private void updateCouponStats(Long couponId) {
        couponMapper.updateCouponStats(couponId);
    }
    
    /**
     * 发放拼团奖励
     */
    private void distributeGroupRewards(CouponGroup group) {
        // TODO: 实现拼团奖励发放逻辑
        log.info("拼券团{}成团成功，开始发放奖励", group.getId());
    }
}
